#include "sensor.h"

#if SENSOR_AUTOBASE
#include "sensor_autobase.h"
#endif


// Private variable definition
sensor_default_param DP = {0};

static uint32 m_u32_prevTime = 0;
static uint32 m_u32_currentTime = 0;

static sensor_period_timer m_t_sampleTimer = {
    0,
    SECONDS_PER_HOUR / SAMPLES_PER_HOUR / LEVEL1_SAMPLE_SECONDS
};

static sensor_period_timer m_t_glucoseTimer = {
    0,
    SECONDS_PER_HOUR / GLUCOSE_PER_HOUR / LEVEL1_SAMPLE_SECONDS
};

// Private function declaration

static logical UpdateAndChekTime( uint32 u32_time );


// Function definition
void Sensor_UpdateParam(uint16 u16_CalFactor, sint16 s16_Offset)
{
    DP.OFFSET_DEFAULT             = s16_Offset;
    DP.CAL_FACTOR_DEFAULT         = u16_CalFactor;
}

void Sensor_ResetParam(void)
{
     DP.EXPIRATION_TIME            = DP_EXPIRATION_TIME;
     DP.CAL_FACTOR_DEFAULT         = 0;
     DP.OFFSET_DEFAULT             = DP_OFFSET_DEFAULT;
     DP.CAL_FACTOR_1               = DP_CAL_FACTOR_1;
     DP.CAL_FACTOR_2               = DP_CAL_FACTOR_2;
     DP.CAL_FACTOR_3               = DP_CAL_FACTOR_3;
     DP.CAL_FACTOR_4               = DP_CAL_FACTOR_4;
     DP.CAL_FACTOR_5               = DP_CAL_FACTOR_5;
     DP.CAL_FACTOR_HOURS2          = DP_CAL_FACTOR_HOURS2;
     DP.CAL_FACTOR_HOURS3          = DP_CAL_FACTOR_HOURS3;
     DP.CAL_FACTOR_HOURS4          = DP_CAL_FACTOR_HOURS4;
     DP.OFFSET_1                   = DP_OFFSET_1;
     DP.OFFSET_2                   = DP_OFFSET_2;
     DP.OFFSET_3                   = DP_OFFSET_3;
     DP.OFFSET_4                   = DP_OFFSET_4;
     DP.OFFSET_5                   = DP_OFFSET_5;
     DP.OFFSET_HOURS2              = DP_OFFSET_HOURS2;
     DP.OFFSET_HOURS3              = DP_OFFSET_HOURS3;
     DP.OFFSET_HOURS4              = DP_OFFSET_HOURS4;
     DP.INITIALIZAION_BIAS         = DP_INITIALIZAION_BIAS;
     DP.ISIG_REF_DEFAULT           = DP_ISIG_REF_DEFAULT;
     DP.ISIG_NONLINEAR_C1          = DP_ISIG_NONLINEAR_C1;
     DP.ISIG_NONLINEAR_C0          = DP_ISIG_NONLINEAR_C0;
     DP.CAL_FACTOR_LOWER_SCALE     = DP_CAL_FACTOR_LOWER_SCALE;
     DP.CAL_FACTOR_UPPER_SCALE     = DP_CAL_FACTOR_UPPER_SCALE;
     DP.SENS_FACTOR_LOWER          = DP_SENS_FACTOR_LOWER;
     DP.SENS_FACTOR_UPPER          = DP_SENS_FACTOR_UPPER;
     DP.REF_REAL_LOWER             = DP_REF_REAL_LOWER;
     DP.REF_REAL_UPPER             = DP_REF_REAL_UPPER;
     DP.REF_REAL_NEW_SENSOR        = DP_REF_REAL_NEW_SENSOR;
     DP.REF_REAL_DEFAULT           = DP_REF_REAL_DEFAULT;
     DP.REF_REAL_FACTOR            = DP_REF_REAL_FACTOR;
     DP.REF_REAL_CHANGE_START_HOUR = DP_REF_REAL_CHANGE_START_HOUR;
     DP.REF_REAL_CHANGE_PER_HOUR   = DP_REF_REAL_CHANGE_PER_HOUR;
     DP.REF_REAL_SENS_CHANGED      = DP_REF_REAL_SENS_CHANGED;
     DP.REF_IMAG_LOWER             = DP_REF_IMAG_LOWER;
     DP.REF_IMAG_UPPER             = DP_REF_IMAG_UPPER;
     DP.REF_IMAG_DEFAULT           = DP_REF_IMAG_DEFAULT;
     DP.REF_IMAG_FACTOR            = DP_REF_IMAG_FACTOR;
     DP.REF_IMAG_SENS_CHANGED      = DP_REF_IMAG_SENS_CHANGED;
     DP.REF_IMAG_OFFSET            = DP_REF_IMAG_OFFSET;
}

logical Sensor_ParamValid(void)
{
    return (   WITHIN( DP.EXPIRATION_TIME,              1,                                   30 * SECONDS_PER_DAY               )
            &&(WITHIN( DP.CAL_FACTOR_DEFAULT,               CAL_FACTOR_LOWER_THRESHOLD,           CAL_FACTOR_UPPER_THRESHOLD    ) || DP.CAL_FACTOR_DEFAULT == 0)
            && WITHIN( DP.OFFSET_DEFAULT,             -10 * PERCISION,                       10 * PERCISION                     )
            && WITHIN( DP.CAL_FACTOR_1,                20 * PERCISION / 100 ,                 3 * PERCISION                     )
            && WITHIN( DP.CAL_FACTOR_2,                20 * PERCISION / 100 ,                 3 * PERCISION                     )
            && WITHIN( DP.CAL_FACTOR_3,                20 * PERCISION / 100 ,                 3 * PERCISION                     )
            && WITHIN( DP.CAL_FACTOR_4,                20 * PERCISION / 100 ,                 3 * PERCISION                     )
            && WITHIN( DP.CAL_FACTOR_5,                20 * PERCISION / 100 ,                 3 * PERCISION                     )
            && WITHIN( DP.CAL_FACTOR_HOURS2,            0,                                   20 * HOURS_PER_DAY * PERCISION     )
            && WITHIN( DP.CAL_FACTOR_HOURS3,                DP.CAL_FACTOR_HOURS2,            20 * HOURS_PER_DAY * PERCISION     )
            && WITHIN( DP.CAL_FACTOR_HOURS4,                DP.CAL_FACTOR_HOURS3,            20 * HOURS_PER_DAY * PERCISION     )
            && WITHIN( DP.OFFSET_1,                   -10 * PERCISION,                       10 * PERCISION      )
            && WITHIN( DP.OFFSET_2,                   -10 * PERCISION,                       10 * PERCISION                     )
            && WITHIN( DP.OFFSET_3,                   -10 * PERCISION,                       10 * PERCISION                     )
            && WITHIN( DP.OFFSET_4,                   -10 * PERCISION,                       10 * PERCISION                     )
            && WITHIN( DP.OFFSET_5,                   -10 * PERCISION,                       10 * PERCISION                     )
            && WITHIN( DP.OFFSET_HOURS2,                0,                                   20 * HOURS_PER_DAY * PERCISION     )
            && WITHIN( DP.OFFSET_HOURS3,                    DP.OFFSET_HOURS2,                20 * HOURS_PER_DAY * PERCISION     )
            && WITHIN( DP.OFFSET_HOURS4,                    DP.OFFSET_HOURS3,                20 * HOURS_PER_DAY * PERCISION     )
            && WITHIN( DP.INITIALIZAION_BIAS,           0,                                   80 * PERCISION / 100               )
            && WITHIN( DP.ISIG_REF_DEFAULT,             0,                                   10 * PERCISION                     )
            && WITHIN( DP.ISIG_NONLINEAR_C1,            0,                                   50 * PERCISION                     )
            && WITHIN( DP.ISIG_NONLINEAR_C0,            0,                                  100 * PERCISION                     )
            && WITHIN( DP.CAL_FACTOR_LOWER_SCALE,       0 ,                                   1 * PERCISION                     )
            && WITHIN( DP.CAL_FACTOR_UPPER_SCALE,       1 * PERCISION,                        5 * PERCISION                     )
            && WITHIN( DP.SENS_FACTOR_LOWER,                CAL_FACTOR_LOWER_THRESHOLD ,      1 * PERCISION                     )
            && WITHIN( DP.SENS_FACTOR_UPPER,            1 * PERCISION,                            CAL_FACTOR_UPPER_THRESHOLD    )
            && WITHIN( DP.REF_REAL_NEW_SENSOR,          0,                                  326 * PERCISION                     )
            && WITHIN( DP.REF_REAL_DEFAULT,            20 * PERCISION,                      250 * PERCISION                     )
            && WITHIN( DP.REF_REAL_FACTOR,             -5 * PERCISION,                        5 * PERCISION                     )
            && WITHIN( DP.REF_REAL_CHANGE_START_HOUR,   0,                                   20 * HOURS_PER_DAY * PERCISION     )
            && WITHIN( DP.REF_REAL_CHANGE_PER_HOUR,    -1 * PERCISION,                        1 * PERCISION                     )
            && WITHIN( DP.REF_REAL_SENS_CHANGED,        0,                                  326 * PERCISION                     )
            && WITHIN( DP.REF_IMAG_DEFAULT,           -20 * PERCISION,                      150 * PERCISION                     )
            && WITHIN( DP.REF_IMAG_FACTOR,            -20 * PERCISION,                       20 * PERCISION                     )
            && WITHIN( DP.REF_IMAG_SENS_CHANGED,        0,                                  326 * PERCISION                     ));
}

void Sensor_Initialize( uint32 u32_time,
                        uint32 u32_calTime,
                        fixed32 f32_calFactor,
                        fixed32 f32_offset,
                        comp64 c64_refImp,
                        fixed32 f32_naBias,
#if SENSOR_AUTOBASE
                        uint32 u32_num,
                        float f_base0,
                        float f_base,
#endif
                        int32 (*pf_ReadVCounter)(void),
                        int32 (*pf_ReadISig1)(void),
                        int32 (*pf_ReadISig2)(void),
                        uint64 (*pf_ReadImpedance)(uint) )
{
    m_u32_prevTime = 0;
    m_u32_currentTime = u32_time;
    Electrode_Initialize( pf_ReadVCounter,
                          pf_ReadISig1,
                          pf_ReadISig2 );
    CE_Initialize();
    WE_Initialize();
    Impedance_Initialize( u32_calTime, c64_refImp, pf_ReadImpedance );
    Glu_Initialize( u32_calTime, f32_calFactor, f32_offset, f32_naBias );
#if SENSOR_AUTOBASE
    AB_Init();
    AB_Start(u32_calTime > 0, u32_num, f_base0, f_base);
#endif
}

void Sensor_Reset( uint32 u32_time,
                   fixed32 f32_calFactor,
                   fixed32 f32_offset,
                   fixed32 f32_naBias
#if SENSOR_AUTOBASE
                  ,uint32 u32_num,
                   float f_base0,
                   float f_base
#endif
                   )
{
    m_u32_prevTime = 0;
    m_u32_currentTime = u32_time;

    Impedance_Reset();
    Glu_Initialize( 0, f32_calFactor, f32_offset, f32_naBias );
#if SENSOR_AUTOBASE
    AB_Start(0, u32_num, f_base0, f_base);
#endif
}


logical Sensor_MeasureGlucose( uint32 u32_time )
{
    if (UpdateAndChekTime( u32_time ) != L_TRUE)
        return L_FALSE;
    Electrode_Read();
    if( CountToDo( &m_t_sampleTimer ) )
    {
        CE_Update();
        WE_UpdateIsig( u32_time );
    }
    if( CountToDo( &m_t_glucoseTimer ) )
    {
        Glu_CalculateGlucose( u32_time );
    }
    return Sensor_IsGlucoseUpdated();
}


logical Sensor_MeasureImpedance( uint32 u32_time )
{
    if (UpdateAndChekTime( u32_time ) != L_TRUE)
        return L_FALSE;
    Electrode_SetInterrupted();
    return Impedance_Update( u32_time );
}


logical Sensor_Calibration( uint32 u32_time,
                            fixed32 f32_glucose
#if SENSOR_AUTOBASE
                           ,uint8 u8_limitScale
#endif
                            )
{
#if SENSOR_AUTOBASE
    if ( Glu_Calibration( u32_time, f32_glucose, u8_limitScale ) )
#else
    if ( Glu_Calibration( u32_time, f32_glucose) )
#endif
    {
        Impedance_SensReset( u32_time );
        return L_TRUE;
    }
    else
    {
        return L_FALSE;
    }
}


static logical UpdateAndChekTime( uint32 u32_time )
{
    if ( u32_time + SECONDS_PER_HOUR/SAMPLES_PER_HOUR < m_u32_currentTime ||
         u32_time > m_u32_currentTime + DP.EXPIRATION_TIME )
        return L_FALSE;
    m_u32_prevTime = m_u32_currentTime;
    m_u32_currentTime = u32_time;
    return L_TRUE;
}


logical Sensor_IsGlucoseUpdated()
{
    return CheckToDo( &m_t_glucoseTimer );
}
